#include <webx/menu.h>
#include <dbentity/T_XG_USER.h>
#include <dbentity/T_XG_MENU.h>
#include <dbentity/T_XG_NOTE.h>
#include <dbentity/T_XG_CODE.h>
#include <dbentity/T_XG_PARAM.h>
#include <dbentity/T_XG_GROUP.h>
#include <dbentity/T_XG_DBETC.h>

static HttpServer* app = HttpServer::Instance();

static BOOL InitNewUser(const char* user)
{
	try
	{
		CT_XG_USER tmp;

		tmp.init(webx::GetDBConnect());

		tmp.user = user;

		CHECK_FALSE_RETURN(tmp.find() && tmp.next());

		string dbid = tmp.dbid.val();

		CHECK_FALSE_RETURN(tmp.find("USER='template'") && tmp.next());

		int res;
		CT_XG_NOTE note;
		CT_XG_CODE code;
		CT_XG_PARAM param;
		vector<CT_XG_NOTE> notevec;
		vector<CT_XG_CODE> codevec;
		vector<CT_XG_PARAM> paramvec;
		sp<DBConnect> srconn = tmp.dbid.val().empty() ? tmp.getHandle() : webx::GetDBConnect(tmp.dbid.val());

		if (note.init(srconn) && note.find("USER='template'"))
		{
			while (note.next())
			{
				notevec.push_back(note);
				notevec.back().user = user;
			}
		}
		
		if (code.init(srconn) && code.find("USER='template'"))
		{
			while (code.next())
			{
				codevec.push_back(code);
				codevec.back().user = user;
			}
		}

		if (param.init(srconn) && param.find("ID LIKE 'template.%'"))
		{
			while (param.next())
			{
				paramvec.push_back(param);
				paramvec.back().id = user + param.id.val().substr(8);
			}
		}

		sp<DBConnect> destconn = dbid == tmp.dbid.val() ? srconn : webx::GetDBConnect(dbid);

		for (auto& tab : notevec)
		{
			tab.init(destconn, false);

			for (int i = 0; i < 5; i++)
			{
				tab.id = DateTime::GetBizId();
				if ((res = tab.insert()) >= 0) break;
			}
		}

		for (auto& tab : codevec)
		{
			tab.init(destconn, false);

			for (int i = 0; i < 5; i++)
			{
				tab.id = DateTime::GetBizId();
				if ((res = tab.insert()) >= 0) break;
			}
		}

		for (auto& tab : paramvec)
		{
			tab.init(destconn, false);
			tab.insert();
		}

		return true;
	}
	catch (Exception e)
	{
		return false;
	}
};
static void AsyncInitNewUser(const char* user)
{
	string newuser = user;

	stdx::async([=](){
		try{
			InitNewUser(newuser.c_str());
		}
		catch(Exception e)
		{
			LogTrace(eERR, "initialize user[%s] failed[%d][%s]", newuser.c_str(), e.getErrorCode(), e.getErrorString());
		}
	});
}
static int GetString(const string& val, char* dest, int size)
{
	if (--size > val.length()) size = val.length();

	if (size > 0)
	{
		strcpy(dest, val.c_str());
	}
	else
	{
		*dest = 0;
	}

	return val.length();
};
static int GetParameter(const char* id, char* dest, int size)
{
	sp<Session> session = webx::GetLocaleSession("SYSTEM_PARAMETER", 600);

	if (!session) return XG_SYSERR;

	string val = session->get(id);

	if (val.length() > 0) return GetString(val, dest, size);

	string sqlcmd = "SELECT PARAM FROM T_XG_PARAM WHERE ID=?";

	try
	{
		if (webx::GetDBConnect()->select(val, sqlcmd, id) < 0) return XG_SYSERR;

		session->set(id, val = stdx::translate(val));

		return GetString(val, dest, size);
	}
	catch(Exception e)
	{
		return XG_SYSBUSY;
	}
}
static int GetConfileContent(const char* title, char* dest, int size)
{
	sp<Session> session = webx::GetLocaleSession("SYSTEM_CONFILE", 600);

	if (!session) return XG_SYSERR;

	string val = session->get(title);

	if (val.length() > 0) return GetString(val, dest, size);

	string sqlcmd = "SELECT CONTENT FROM T_XG_CONF WHERE FOLDER='WEBPAGE' AND TITLE=?";
	
	try
	{
		if (webx::GetDBConnect()->select(val, sqlcmd, title) < 0) return XG_SYSERR;

		session->set(title, val = stdx::translate(val));

		return GetString(val, dest, size);
	}
	catch(Exception e)
	{
		return XG_SYSBUSY;
	}
}
static DBConnectPool* GetDBConnectPool(const char* id)
{
	static SpinMutex mtx;
	static map<string, DBConnectPool*> dbmap;

	char buffer[1024];
	SpinLocker lk(mtx);
	auto it = dbmap.find(id);

	if (it != dbmap.end()) return it->second;

	ConfigFile cfg;
	CT_XG_DBETC tab;
	sp<DBConnect> dbconn;

	try
	{
		dbconn = webx::GetDBConnect();
	}
	catch(Exception e)
	{
		return NULL;
	}

	tab.init(dbconn);
	tab.id = id;

	if (!tab.find())
	{
		LogTrace(eERR, "query database pool[%s] failed", id);

		return NULL;
	}

	if (!tab.next())
	{
		LogTrace(eERR, "database pool[%s] undefined", id);

		return NULL;
	}

	if (tab.enabled.val() <= 0)
	{
		LogTrace(eERR, "database pool[%s] disable", id);

		return NULL;
	}

	string key = "DLLPATH_" + stdx::toupper(tab.type.val());

	if (GetParameter(key.c_str(), buffer, sizeof(buffer)) > 0)
	{
		cfg.setVariable("DLLPATH", buffer);
	}
	else
	{
		string path = stdx::tolower(tab.type.val());

		path = stdx::translate("$PRODUCT_HOME/dll/libdbx." + path + "pool.so");

		cfg.setVariable("DLLPATH", path);
	}
	
	cfg.setVariable("HOST", tab.host.val());
	cfg.setVariable("USER", tab.user.val());
	cfg.setVariable("NAME", tab.name.val());
	cfg.setVariable("PORT", (int)tab.port.val());
	cfg.setVariable("CHARSET", tab.charset.val());
	cfg.setVariable("PASSWORD", tab.passwd.val());

	DBConnectPool* pool = DBConnectPool::Create(cfg);

	if (pool == NULL)
	{
		LogTrace(eERR, "create database pool[%s] failed", id);

		return NULL;
	}

	return dbmap[id] = pool;
}
static int LoadMenuSet(set<MenuItem>& dataset)
{
	static time_t tm = 0;
	static SpinMutex mtx;
	static set<MenuItem> menuset;

	SpinLocker lk(mtx);

	if (tm + 60 > time(NULL) && Process::GetObject("HTTP_MENU_SET"))
	{
		dataset.insert(menuset.begin(), menuset.end());
	}
	else
	{
		MenuItem menu;
		CT_XG_MENU menutab;
		sp<DBConnect> dbconn;

		try
		{
			dbconn = webx::GetDBConnect();
		}
		catch(Exception e)
		{
			return XG_SYSBUSY;
		}

		menuset.clear();
		menutab.init(dbconn);

		if (!menutab.find("ENABLED>0"))
		{
			LogTrace(eERR, "load menu set failed");

			return XG_SYSERR;
		}
		
		while (menutab.next())
		{
			menu.id = menutab.id.val();
			menu.url = menutab.url.val();
			menu.icon = menutab.icon.val();
			menu.title = menutab.title.val();
			menu.folder = menutab.folder.val();
			menu.position = menutab.position.val();
			menuset.insert(menu);
		}

		Process::SetObject("HTTP_MENU_SET", &menuset);

		dataset.insert(menuset.begin(), menuset.end());

		LogTrace(eINF, "load menu set success");
		
		tm = time(NULL);
	}
	
	return dataset.size();
}
static int LoadGroupSet(set<GroupItem>& dataset)
{
	static time_t tm = 0;
	static SpinMutex mtx;
	static set<GroupItem> grpset;

	SpinLocker lk(mtx);

	if (tm + 60 > time(NULL) && Process::GetObject("HTTP_GROUP_SET"))
	{
		dataset.insert(grpset.begin(), grpset.end());
	}
	else
	{
		GroupItem grp;
		CT_XG_GROUP grptab;
		sp<DBConnect> dbconn;

		try
		{
			dbconn = webx::GetDBConnect();
		}
		catch(Exception e)
		{
			return XG_SYSBUSY;
		}

		grpset.clear();
		grptab.init(dbconn);

		if (!grptab.find("ENABLED>0"))
		{
			LogTrace(eERR, "load group set failed");

			return XG_SYSERR;
		}
		
		string val;
		vector<string> vec;
		
		while (grptab.next())
		{
			grp.id = grptab.id.val();
			grp.menus.clear();

			if ((val = grptab.menulist.val()).length() > 0)
			{
				vec.clear();
				
				if (stdx::split(vec, val, ",") > 0)
				{
					for (size_t i = 0; i < vec.size(); i++)
					{
						if ((val = stdx::trim(vec[i])).length() > 0)
						{
							grp.menus.push_back(val);
						}
					}
				}
			}
			
			grpset.insert(grp);
		}

		Process::SetObject("HTTP_GROUP_SET", &grpset);

		dataset.insert(grpset.begin(), grpset.end());

		LogTrace(eINF, "load group set success");
		
		tm = time(NULL);
	}

	return dataset.size();
}

HTTP_PLUGIN_INIT({
	Process::SetObject("HTTP_GET_CONFILE_CONTENT", (void*)GetConfileContent);
	Process::SetObject("HTTP_ASYNC_INIT_NEW_USER", (void*)AsyncInitNewUser);
	Process::SetObject("HTTP_GET_DBCONNECT_POOL", (void*)GetDBConnectPool);
	Process::SetObject("HTTP_GET_GROUP_SET_FUNC", (void*)LoadGroupSet);
	Process::SetObject("HTTP_GET_MENU_SET_FUNC", (void*)LoadMenuSet);
	Process::SetObject("HTTP_GET_PARAMETER", (void*)GetParameter);

	if (app->getId() > 0)
	{
		set<MenuItem> menuset;
		set<GroupItem> groupset;

		if (LoadGroupSet(groupset) < 0) return XG_SYSERR;

		if (LoadMenuSet(menuset) < 0) return XG_SYSERR;
	}

	return XG_OK;
})